package rest

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	"gitee.com/hexug/go-chain-restful-client/common/logger"
)

// 修改基础路径
func (c *Client) SetBaseURL(u string) *Client {
	u1 := strings.TrimRight(u, "/")
	if !strings.HasPrefix(u1, "http://") && !strings.HasPrefix(u1, "https://") {
		// 添加默认的http://作为协议前缀
		u1 = "http://" + strings.TrimSpace(u1)
	}
	parsedURL, err := url.Parse(u1)
	if err != nil {
		c.log.L().Errorw("url转化失败", err)
		os.Exit(1)
	}
	scheme := parsedURL.Scheme
	if scheme == "" || (scheme != "http" && scheme != "https") {
		// 如果方案不为空且不是http或https，则认为输入的URL无效
		fmt.Println("无效URL:", u1)
		os.Exit(1)
	}
	c.baseURL = u1
	return c
}

// 方便重复利用
func (c *Client) Clone() *Client {
	cloned := &Client{}
	*cloned = *c
	return cloned
}

// 方便重复利用
func (c *Client) Group(urlPath string) *Client {
	cloned := c.Clone()
	baseURL, err := url.JoinPath(c.baseURL, urlPath)
	if err != nil {
		c.log.L().Errorw(err.Error())
		os.Exit(1)
	}
	cloned.baseURL = baseURL
	return cloned
}

// 设置header
func (c *Client) SetHeader(key string, values ...string) *Client {
	if key == "" {
		c.log.L().Errorw("设置的header不合法")
		return c
	}
	if c.headers == nil {
		c.headers = http.Header{}
	}
	c.headers.Del(key)
	for _, value := range values {
		c.headers.Add(key, value)
		c.log.L().Debugf("添加header：%s = %s", key, value)
	}
	return c
}

// 设置cookie
func (c *Client) SetCookie(cs ...*http.Cookie) *Client {
	if c.cookies == nil {
		c.cookies = make([]*http.Cookie, 0)
	}
	c.cookies = append(c.cookies, cs...)
	c.log.L().Debugf("已经设置cookie：%v", cs)
	return c
}

// 设置Content-Type
func (c *Client) SetContentType(contentType string) *Client {
	c.SetHeader("Content-Type", contentType)
	c.log.L().Debugf("设置Content-Type为：%s", contentType)
	return c
}

func (c *Client) SetBasicAuth(username, password string) *Client {
	c.authType = BasicAuth
	c.user = &User{Username: username, Password: password}
	c.log.L().Debugf("认证方式为 %s,用户名和密码为%s/%s", c.authType, username, password)
	return c
}

func (c *Client) SetBearerTokenAuth(token string) *Client {
	c.authType = BearerToken
	c.token = token
	c.log.L().Debugf("认证方式为 %s,token为 %s", c.authType, token)
	return c
}

func (c *Client) SetUserAgent(user_agent string) *Client {
	if user_agent == "" {
		c.log.L().Errorw("设置的user-agent不合法")
		return c
	}
	c.SetHeader("User-Agent", user_agent)
	c.user_agent = user_agent
	return c
}

func (c *Client) SetLogLevel(l logger.Level) *Client {
	c.log.SetLevel(l)
	c.log.L().Debugf("日志级别修改为 %s", l)
	return c
}
func (c *Client) SetLogSaveFile(s bool) *Client {
	c.log.SetSaveFile(s)
	if s {
		c.log.L().Debugw("日志将保存为文件")
	} else {
		c.log.L().Debugw("日志将只在控制台输出")
	}
	return c
}
func (c *Client) SetLogDirPath(path string) *Client {
	ok := c.log.SetDirPath(path)
	if ok {
		c.log.L().Debugf("日志将保存为文件,日志文件目录为：%s", path)
	} else {
		c.log.L().Debugw("日志将只在控制台输出,无法保存为文件。")
	}
	return c
}

func (c *Client) SetTimeout(t time.Duration) *Client {
	c.client.Timeout = t
	c.log.L().Debugf("设置客户端超时 %s", t)
	return c
}

func (c *Client) Method(verb Method) *Request {
	if c.user_agent == "" {
		c.SetUserAgent("go-chain-restful-client/1.0")
	}
	return NewRequest(c).Method(verb)
}

// Post begins a POST request. Short for c.Verb("POST").
func (c *Client) Post(path string) *Request {
	return c.Method(POST).URL(path)
}

// Put begins a PUT request. Short for c.Verb("PUT").
func (c *Client) Put(path string) *Request {
	return c.Method(PUT).URL(path)
}

// Patch begins a PATCH request. Short for c.Verb("Patch").
func (c *Client) Patch(path string) *Request {
	return c.Method(PATCH).URL(path)
}

// Get begins a GET request. Short for c.Verb("GET").
func (c *Client) Get(path string) *Request {
	return c.Method(GET).URL(path)
}

// Delete begins a DELETE request. Short for c.Verb("DELETE").
func (c *Client) Delete(path string) *Request {
	return c.Method(DELETE).URL(path)
}

// 禁止在遇到3XX返回码时自动跳转
func (c *Client) SetNoAutoRedirect() *Client {
	c.client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
		return http.ErrUseLastResponse
	}
	c.log.L().Debugln("设置禁用自动跳转")
	return c
}

// 设置忽略证书
func (c *Client) SetIgnoreCert() *Client {
	transport := reflect.ValueOf(c.client.Transport)
	if transport.IsValid() && !transport.IsNil() {
		field := transport.Elem().FieldByName("TLSClientConfig")
		if field.IsValid() && !field.IsNil() {
			tlsConfig, ok := field.Interface().(*tls.Config)
			if ok {
				tlsConfig.InsecureSkipVerify = true
			}
		} else {
			tlsConfig := &tls.Config{
				InsecureSkipVerify: true,
			}
			field.Set(reflect.ValueOf(tlsConfig))
		}
	} else {
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		}
		c.client.Transport = tr
	}
	c.log.L().Debugln("设置忽略证书")
	return c
}

// 设置TLS最低版本
func (c *Client) SetMinVersionTLS(tlsVersion uint16) *Client {
	transport := reflect.ValueOf(c.client.Transport)
	if transport.IsValid() && !transport.IsNil() {
		field := transport.Elem().FieldByName("TLSClientConfig")
		if field.IsValid() && !field.IsNil() {
			tlsConfig, ok := field.Interface().(*tls.Config)
			if ok {
				tlsConfig.MinVersion = tlsVersion
			}
		} else {
			tlsConfig := &tls.Config{
				MinVersion: tlsVersion,
			}
			field.Set(reflect.ValueOf(tlsConfig))
		}
	} else {
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{
				MinVersion: tlsVersion,
			},
		}
		c.client.Transport = tr
	}
	c.log.L().Debugln("已经修改为低版本为：", tlsVersion)
	return c
}

// 设置证书
func (c *Client) SetCert(caPath, clientCrtPath, clientKeyPath string) *Client {
	cert, err := tls.LoadX509KeyPair(clientCrtPath, clientKeyPath)
	if err != nil {
		c.log.L().Errorln("未能加载客户端证书：", err.Error())
		os.Exit(1)
	}
	caCert, err := os.ReadFile(caPath)
	if err != nil {
		c.log.L().Errorln("无法读取CA证书：", err.Error())
		os.Exit(1)
	}
	caCertPool := x509.NewCertPool()
	caCertPool.AppendCertsFromPEM(caCert)
	transport := reflect.ValueOf(c.client.Transport)
	if transport.IsValid() && !transport.IsNil() {
		field := transport.Elem().FieldByName("TLSClientConfig")
		if field.IsValid() && !field.IsNil() {
			tlsConfig, ok := field.Interface().(*tls.Config)
			if ok {
				tlsConfig.Certificates = []tls.Certificate{cert}
				tlsConfig.RootCAs = caCertPool
			}
		} else {
			tlsConfig := &tls.Config{
				Certificates: []tls.Certificate{cert},
				RootCAs:      caCertPool,
			}
			field.Set(reflect.ValueOf(tlsConfig))
		}
	} else {
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{
				Certificates: []tls.Certificate{cert},
				RootCAs:      caCertPool,
			},
		}
		c.client.Transport = tr
	}
	c.log.L().Debugln("已经添加证书：", caPath, clientCrtPath, clientKeyPath)
	return c
}

// 自定义的 CheckRedirect 函数
func (c *Client) retryOnRedirect(req *http.Request, via []*http.Request) error {
	if len(via) >= 10 { // 避免过多的重定向导致无限循环
		return fmt.Errorf("太多重定向")
	}

	// 在每次重定向前检查是否有查询参数需要添加
	if len(via) > 0 {
		lastRequest := via[len(via)-1]
		redirectURL := req.URL
		switch c.severity {
		case FullMatch:
			// 判断上一个请求和当前重定向的 URL 是否相同
			if lastRequest.URL.Host == redirectURL.Host && lastRequest.URL.Path == redirectURL.Path {
				// 将上一个请求的查询参数添加到当前重定向的 URL 中
				query := lastRequest.URL.Query()
				redirectURL.RawQuery = query.Encode()
			}
		case HOSTONLY:
			if lastRequest.URL.Host == redirectURL.Host {
				// 将上一个请求的查询参数添加到当前重定向的 URL 中
				query := lastRequest.URL.Query()
				redirectURL.RawQuery = query.Encode()
			}
		case All:
			// 将上一个请求的查询参数添加到当前重定向的 URL 中
			query := lastRequest.URL.Query()
			redirectURL.RawQuery = query.Encode()
		}

	}
	// 保持原有的 cookie
	for _, cookie := range via[0].Cookies() {
		req.AddCookie(cookie)
	}
	return nil
}

// 自动携带cookie
func (c *Client) SetCarryCookies() *Client {
	cookieJar, err := cookiejar.New(nil)
	if err != nil {
		c.log.L().Errorln(err.Error())
	}
	c.client.Jar = cookieJar
	return c
}

type Severity int8

const (
	// 必须与url的path完全相同
	FullMatch Severity = iota
	// 只需要host部分相同
	HOSTONLY
	// 所有url都可以
	All
)

// 设置当跳转的时候，自动携带请求参数
func (c *Client) SetCarryQueryParameters(severity Severity) *Client {
	c.severity = severity
	c.client.CheckRedirect = c.retryOnRedirect
	return c
}

// 设置代理
func (c *Client) SetProxyUrl(u string) *Client {
	u1 := strings.TrimRight(u, "/")
	if !strings.HasPrefix(u1, "http://") && !strings.HasPrefix(u1, "https://") {
		// 添加默认的http://作为协议前缀
		u1 = "http://" + strings.TrimSpace(u1)
	}
	parsedURL, err := url.Parse(u1)
	if err != nil {
		c.log.L().Errorw("url转化失败", err)
		os.Exit(1)
	}
	scheme := parsedURL.Scheme
	if scheme == "" || (scheme != "http" && scheme != "https") {
		// 如果方案不为空且不是http或https，则认为输入的URL无效
		c.log.L().Errorw("无效URL:", u1)
		os.Exit(1)
	}
	c.proxyScheme = scheme
	c.proxyAdd = parsedURL.Host
	return c
}

func (c *Client) SetProxyPort(port int) *Client {
	c.proxyPort = strconv.Itoa(port)
	return c
}
func (c *Client) SetProxyScheme(scheme string) *Client {
	c.proxyScheme = scheme
	return c
}
func (c *Client) SetProxyAdd(add string) *Client {
	c.proxyAdd = add
	return c
}

func (c *Client) SetDNS(dnsResolver string) *Client {
	transport := reflect.ValueOf(c.client.Transport)
	if transport.IsValid() && !transport.IsNil() {
		field := transport.Elem().FieldByName("DialContext")
		if field.IsValid() && !field.IsNil() {
			tsp := c.client.Transport.(*http.Transport)
			tsp.DialContext = CustomDialContext(dnsResolver)
		} else {
			field.Set(reflect.ValueOf(CustomDialContext(dnsResolver)))
		}
	} else {
		dc := &http.Transport{
			DialContext: CustomDialContext(dnsResolver),
		}
		c.client.Transport = dc
	}
	c.log.L().Debugln("设置DNS Server")
	return c
}
